using UnityEngine;

/// <summary>
/// CreateLocalToWorldMatrix 说明任意 Transform 如何构建 LocalToWorldMatrix
/// </summary>
public class CreateLocalToWorldMatrix : MonoBehaviour
{
    public Transform 本地坐标系;

    void OnGUI()
    {
        GUILayout.Label("本地坐标系.localToWorldMatrix :");
        GUILayout.Label($"{本地坐标系.localToWorldMatrix.ToString("F4")}");
        GUILayout.Label("本地坐标系.worldToLocalMatrix :");
        GUILayout.Label($"{本地坐标系.worldToLocalMatrix.ToString("F4")}");
        GUILayout.Label("================================");

        GUILayout.Label($"本地坐标系.right    : {本地坐标系.right.ToString("F4")}");
        GUILayout.Label($"本地坐标系.up       : {本地坐标系.up.ToString("F4")}");
        GUILayout.Label($"本地坐标系.forward  : {本地坐标系.forward.ToString("F4")}");
        GUILayout.Label($"本地坐标系.scale    : {本地坐标系.localScale.ToString("F4")}");
        GUILayout.Label($"本地坐标系.position : {本地坐标系.position.ToString("F4")}");
        GUILayout.Label("================================");

        GUILayout.Label("通过三轴构建");
        var s = "LocalToWorldMatrix: \n" +
                "(right.x * scale.x , up.x * scale.y , forward.x * scale.z , position.x )\n" +
                "(right.y * scale.x , up.y * scale.y , forward.y * scale.z , position.y )\n" +
                "(right.z * scale.x , up.z * scale.y , forward.z * scale.z , position.z )\n" +
                "( 0 , 0 , 0 , 1 )";
        GUILayout.Label(s);

        var myLocalToWorldMatrix = CreateLocalToWorldMatrixFromTransform(本地坐标系);
        GUILayout.Label("自行构建 localToWorldMatrix == 本地坐标系.localToWorldMatrix : " +
                        $" {myLocalToWorldMatrix == 本地坐标系.localToWorldMatrix}");
        GUILayout.Label($"{myLocalToWorldMatrix.ToString("F4")}");
        GUILayout.Label("================================");

        var myLocalToWorldMatrix2 = Matrix4x4.identity;
        myLocalToWorldMatrix2.SetTRS(本地坐标系.localPosition, 本地坐标系.localRotation, 本地坐标系.localScale);
        GUILayout.Label("Unity 其它 API 构建 localToWorldMatrix == 本地坐标系.localToWorldMatrix : " +
                        $" {myLocalToWorldMatrix2 == 本地坐标系.localToWorldMatrix}");
        GUILayout.Label($"{myLocalToWorldMatrix2.ToString("F4")}");
        GUILayout.Label("================================");

        GUILayout.Label("仅通过 right 向量构建 localToWorldMatrix : ");
        GUILayout.Label($" {CreateLocalToWorldMatrixFromRight(本地坐标系.right, 本地坐标系.lossyScale, 本地坐标系.position).ToString("F4")}");
        GUILayout.Label("这样构建的坐标系 仅在 Z轴处于水平面 且 Y轴向上时 与 原坐标系 一致！！！");
    }

    private Matrix4x4 CreateLocalToWorldMatrixFromTransform(Transform trans)
    {
        return CreateLocalToWorldMatrixFull(
            trans.right,
            trans.up,
            trans.forward,
            trans.lossyScale,
            trans.position);
    }

    private Matrix4x4 CreateLocalToWorldMatrixFull(Vector3 right, Vector3 up, Vector3 forward, Vector3 scale, Vector3 position)
    {
        var myLocalToWorldMatrix = Matrix4x4.identity;
        myLocalToWorldMatrix.SetColumn(0, right * scale.x);
        myLocalToWorldMatrix.SetColumn(1, up * scale.y);
        myLocalToWorldMatrix.SetColumn(2, forward * scale.z);
        myLocalToWorldMatrix.SetColumn(3, new Vector4(
            position.x,
            position.y,
            position.z,
            1));
        return myLocalToWorldMatrix;
    }

    private Matrix4x4 CreateLocalToWorldMatrixFromRight(Vector3 right)
    {
        return CreateLocalToWorldMatrixFromRight(right, Vector3.one);
    }

    private Matrix4x4 CreateLocalToWorldMatrixFromRight(Vector3 right, Vector3 scale)
    {
        return CreateLocalToWorldMatrixFromRight(right, Vector3.one, Vector3.zero);
    }

    private Matrix4x4 CreateLocalToWorldMatrixFromRight(Vector3 right, Vector3 scale, Vector3 position)
    {
        right = Vector3.Normalize(right);

        // 假设 up 向上 并 通过 叉乘 获得 forward
        // 注意 Cross 左手定则！
        // 需要构建 右手坐标系！
        var forward = Vector3.Normalize(Vector3.Cross(right, Vector3.up)); 
        
        // 通过 right 和 forward 获取 up
        var up = Vector3.Cross(forward, right);

        return CreateLocalToWorldMatrixFull(right, up, forward, scale, position);
    }
}